
from typing import TYPE_CHECKING, List, Optional

from chernc.treediff.gumtree.tree.tree import TreeLike
from chernc.treediff.gumtree.tree.tree_metric_computer import TreeMetricComputer
from chernc.treediff.gumtree.tree.tree_metrics import TreeMetrics
from chernc.treediff.gumtree.tree.tree_visitor import TreeVisitor

def _get_node_height(node: TreeLike) -> int:
    if node.is_leaf():
        return 1
    child_heights = [_get_node_height(child) for child in node.get_children()]
    return max(child_heights) + 1

class AbstractTree(TreeLike):
    def __init__(self):
        self.parent: Optional[TreeLike] = None
        self.children: List[TreeLike] = []
        self.metrics: Optional[TreeMetrics] = None

    def __str__(self) -> str:
        if self.has_label():
            return f"{self.get_type()}: {self.get_label()} [{self.get_pos()}, {self.get_end_pos()}]"
        else:
            return f"{self.get_type()} [{self.get_pos()}, {self.get_end_pos()}]"
    
    def to_string(self) -> str:
        if self.has_label():
            return f"{self.get_type()}: {self.get_label()} [{self.get_pos()}, {self.get_end_pos()}]"
        else:
            return f"{self.get_type()} [{self.get_pos()}, {self.get_end_pos()}]"

    def to_tree_string(self) -> str:
        return self.to_string()

    def get_height(self) -> int:
        return _get_node_height(self)
    
    def get_parent(self) -> Optional[TreeLike]:
        return self.parent

    def set_parent(self, parent: Optional[TreeLike]) -> None:
        self.parent = parent

    def set_parent_and_update_children(self, parent: TreeLike) -> None:
        if self.parent is not None:
            self.parent.get_children().remove(self)
        self.parent = parent
        if self.parent is not None:
            self.parent.get_children().append(self)

    def get_children(self) -> List[TreeLike]:
        return self.children

    def set_children(self, children: List[TreeLike]) -> None:
        self.children = children
        for c in children:
            c.set_parent(self)

    def add_child(self, child: TreeLike) -> None:
        self.children.append(child)
        child.set_parent(self)

    def insert_child(self, t: TreeLike, position: int) -> None:
        self.children.insert(position, t)
        t.set_parent(self)

    def get_metrics(self) -> TreeMetrics:
        if self.metrics is None:
            root = self
            if not self.is_root():
                parents = self.get_parents()
                root = parents[-1]
            TreeVisitor.visit_tree(root, TreeMetricComputer())  # This will populate self.metrics
        if self.metrics is None:
            raise Exception("The metric of the tree is None")
        return self.metrics

    def set_metrics(self, metrics: TreeMetrics) -> None:
        self.metrics = metrics

    class EmptyEntryIterator:
        def __iter__(self):
            return self

        def __next__(self):
            raise StopIteration